import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:mutex/mutex.dart';
import 'package:s_utils/s_utils.dart';

class AuthInterceptor extends QueuedInterceptorsWrapper {
  final Dio tokenDio;
  final Dio retryDio;
  final VoidCallback logout;
  final Future<Response> Function(Dio dio) refresh;

  final _lock = Mutex();
  DateTime? refreshAt = null;

  AuthInterceptor({
    required this.tokenDio,
    required this.retryDio,
    required this.logout,
    required this.refresh,
  });

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    String token = SHttp.headerToken;
    if (token != "") options.headers[SHttp.headerTokenKey] = token;
    return handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    final token = response.headers.value(SHttp.headerTokenKey);
    if (token != null) SHttp.headerToken = token;
    return handler.resolve(response);
  }

  @override
  Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
    if (err.response == null) return handler.next(err);
    if (err.response?.statusCode == 401) {
      try {
        _lock.acquire();
        await refreshToken(err);
        Response res = await retryFetch(err, handler);
        _lock.release();
        return handler.resolve(res);
      } catch (e) {
        _lock.release();
        if (e is DioException) {
          return handler.reject(e);
        } else {
          rethrow;
        }
      }
    }
    super.onError(err, handler);
  }

  refreshToken(DioException err) async {
    // 验证是否重复刷新token
    if (refreshAt != null && refreshAt!.add(Duration(seconds: 60)).isAfter(DateTime.now())) {
      logger.d("跳过刷新TOKEN, 因为太快了(${err.requestOptions.uri})");
      return;
    }

    try {
      SharedPrefs.remove('access_token');
      await refresh(tokenDio);
      refreshAt = DateTime.now();
    } on DioException catch (e) {
      if (e.response != null && e.response!.statusCode != null && e.response!.statusCode! ~/ 100 != 2) {
        logout();
      }
    }
  }

  retryFetch(DioException err, ErrorInterceptorHandler handler) async {
    Map<String, dynamic> header = await SHttp.getHeaders();
    String headerKey = SHttp.headerTokenKey;
    err.requestOptions.headers[headerKey] = header[headerKey];
    Response response = await retryDio.fetch(err.requestOptions);
    return response;
  }
}
